package com.smh.demo1.a16;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.framework.ReflectiveMethodInvocation;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.support.GenericApplicationContext;

import java.util.List;

/**
 * @author shiminghui
 * @date 2025/4/19 22:16
 * @description: TODO
 */
public class A16Application {

    //    ExposeInvocationInterceptor 为其他通知准备MethodInvocation对象
    public static void main(String[] args) throws Throwable {

        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("config", Config.class);

        context.registerBean(ConfigurationClassPostProcessor.class);

        context.refresh();

        Target bean = (Target) context.getBean("target");
        bean.read(10);

        System.out.println("-----------------------");
        // 模拟一下
        // 获取AnnotationAwareAspectJAutoProxyCreator
        TestAnnotationAwareAspectJAutoProxyCreator proxyCreator = (TestAnnotationAwareAspectJAutoProxyCreator) context.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
        // 这个类里面的方法是受保护的没办法直接调用,可以写一个子类继承,然后子类写一个新方法调用父类的方法将其暴露出来
        List<Advisor> advisors = proxyCreator.find(Target.class, "target");
        // 获取到了匹配的Advisors集合
//        for (Advisor advisor : advisors) {
//            System.out.println(advisor);
//        }
        System.out.println("-------------------------");
        Target target = new Target();
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setTarget(target);
        proxyFactory.addAdvisors(advisors);
        // 将advice都转化为Interceptor这样的环绕通知
        List<Object> advice = proxyFactory.getInterceptorsAndDynamicInterceptionAdvice(Target.class.getMethod("read", int.class), Target.class);
        for (Object o : advice) {
            System.out.println(o);
            //InterceptorAndDynamicMethodMatcher 这个就是动态通知的切面
        }
        Target proxy = (Target) proxyFactory.getProxy();

        // 代理对象调用时,方法被拦截,然后待用proceed方法执行代理链
        ReflectiveMethodInvocation methodInvocation = new ReflectiveMethodInvocation(proxy, target, Target.class.getMethod("read", int.class), new Object[]{10}, Target.class, advice) {
        };
        Object proceed = methodInvocation.proceed();
//
//        proxy.read(10);

    }

    // 这里继承使用是因为父类的方法是受保护的
    static class TestAnnotationAwareAspectJAutoProxyCreator extends AnnotationAwareAspectJAutoProxyCreator {
        public List<Advisor> find(Class clazz, String beanName) {
            List<Advisor> eligibleAdvisors = super.findEligibleAdvisors(clazz, beanName);
            return eligibleAdvisors;
        }

        public Object wrap(Object bean, String beanName, String cacheKey) {
            Object o = super.wrapIfNecessary(bean, beanName, cacheKey);
            return o;
        }
    }

    @Configuration
    static class Config {
        @Bean
        public AnnotationAwareAspectJAutoProxyCreator annotationAwareAspectJAutoProxyCreator() {
            return new TestAnnotationAwareAspectJAutoProxyCreator();
        }

        @Bean
        public Target target() {
            return new Target();
        }

        @Bean
        public MyAspect myAspect() {
            return new MyAspect();
        }
    }

    static class Target {
        public void read(int sum) {
            System.out.println("read:" + sum);
        }
    }

    @Aspect
    static class MyAspect {

        @Before("execution(* read(..))&& args(sum)") // 可以拿到方法调用时的参数,根据参数值进行不同的代理操作,即动态代理
        public void before2(int sum) {
            System.out.println("before 携带参数,(动态代理):" + sum);
        }

        @Before("execution(* read(..))")
        public void before() {
            System.out.println("before");
        }

        @After("execution(* read(..))")
        public void after() {
            System.out.println("after");
        }

        @AfterReturning("execution(* read(..))")
        public void afterReturning() {
            System.out.println("afterReturning");
        }

        @AfterThrowing("execution(* read(..))")
        public void afterThrowing() {
            System.out.println("afterThrowing");
        }

        @Around("execution(* read(..))")
        public Object around(ProceedingJoinPoint pjp) {
            System.out.println("around before");
            try {
                Object proceed = pjp.proceed();
                System.out.println("around after");
                return proceed;
            } catch (Throwable e) {
                throw new RuntimeException(e);
            }
        }
    }


}
